package com.express.gateway;

import com.express.gateway.cluster.Cluster;
import com.express.gateway.cluster.ClusterFactory;
import com.express.gateway.common.EventBusTopicConst;
import com.express.gateway.common.util.PathUtil;
import com.express.gateway.verticle.*;
import io.vertx.core.*;
import io.vertx.core.json.JsonObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static com.express.gateway.common.CommonCons.KEY_CLUSTER;

/**
 * Exp-Inlet的程序入口
 *
 * @author flower
 */
public class MasterVerticle extends AbstractVerticle {
    private static final Logger LOG = LogManager.getLogger(MasterVerticle.class);
    /**
     * 使用集群的模式的默认值
     */
    private final String CLUSTER_TYPE = "NONE";

    public static final String CLI_CONFIG_KEY = "cliConfig";
    public static final String DATA_CONFIG_KEY = "dataConfig";
    public static final String CLIENT_CONFIG_KEY = "clientConfig";
    public static final String VERTICLE_CONFIG_KEY = "verticleConfig";

    @Override
    public void start(Promise<Void> fut) throws Exception {

        System.out.println("========== ***** start Exp-Inlet ***** ==========");
        getConfig(conf -> {
            if (conf.failed()) {
                System.out.println("start Exp-Inlet unsuccessful");
                LOG.error("Failed to get config", conf.cause());
                fut.fail(conf.cause());
                return;
            }
            try {
                // verticle的配置信息
                JsonObject option = conf.result().getJsonObject(VERTICLE_CONFIG_KEY, new JsonObject());
                // CLI配置文件
                JsonObject cliConfig = conf.result().getJsonObject(CLI_CONFIG_KEY, getDefaultCLIConfig());
                // 数据的配置文件
                JsonObject dataConfig = conf.result().getJsonObject(DATA_CONFIG_KEY, getDefaultDataConfig());
                // 客户端的配置文件
                JsonObject clientConfig = conf.result().getJsonObject(CLIENT_CONFIG_KEY, getDefaultClientConfig());

                @SuppressWarnings("rawtypes")
                List<Future> futures = new ArrayList<>();
                // 启动系统服务Verticle
                futures.add(Future.<String>future(sysInfo -> {
                    vertx.deployVerticle(MetricsTraceVerticle.class.getName(), new DeploymentOptions(option), sysInfo);
                }));
                // 启动数据服务Verticle
                futures.add(Future.<String>future(data -> {
                    vertx.deployVerticle(DataBaseAccessVerticle.class.getName(), new DeploymentOptions(option).setConfig(dataConfig), data);
                }));
                // 启动应用服务Verticle
                futures.add(Future.<String>future(deploy -> {
                    vertx.deployVerticle(AppCtrlVerticle.class.getName(), new DeploymentOptions(option), deploy);
                }));
                // 启动CLI服务Verticle
                futures.add(Future.<String>future(cli -> {
                    vertx.deployVerticle(ClientCliVerticle.class.getName(), new DeploymentOptions(option), cli);
                }));
                // 启动CLI服务Verticle
                futures.add(Future.<String>future(client -> {
                    vertx.deployVerticle(ClientWebVerticle.class.getName(), new DeploymentOptions(option).setConfig(clientConfig), client);
                }));
                futures.add(Future.<String>future(client -> {
                    vertx.deployVerticle(ClientCurlVerticle.class.getName(), new DeploymentOptions(option).setConfig(clientConfig), client);
                }));
                deployVerticles(fut, futures, cliConfig, clientConfig);
            } catch (Exception e) {
                System.out.println("start Exp-Inlet unsuccessful");
                LOG.error("Failed to get config: {}", e.getMessage(), e);
                fut.fail(e);
            }
        });
    }

    /**
     * 获取配置文件
     *
     * @param promise
     * @param futures
     * @param cliConfig
     * @param clientConfig
     */
    @SuppressWarnings("rawtypes")
    private void deployVerticles(Promise<Void> promise, List<Future> futures, JsonObject cliConfig, JsonObject clientConfig) {
        CompositeFuture.all(futures)
                .onSuccess(res -> {
                    runCLICommand(cliConfig, cliRes -> {
                        if (cliRes.succeeded()) {
                            System.out.println("start Exp-Inlet successful");
                            int svrPort = clientConfig.getInteger("clientPort", 5256);
                            LOG.info("The Exp-Inlet server running on port: {}, http://127.0.0.1:{}", svrPort, svrPort);
                            promise.complete();
                        } else {
                            LOG.error(cliRes.cause().getMessage(), cliRes.cause());
                            promise.fail(cliRes.cause());
                        }
                    });
                }).onFailure(ex -> {
                    LOG.error("start Exp-Inlet unsuccessful :{}", ex.getMessage(), ex);
                    promise.fail(ex.getCause());
                });
    }

    /**
     * 执行CLI的相应指令
     *
     * @param config 配置文件
     * @param handler 回调函数
     */
    public void runCLICommand(JsonObject config, Handler<AsyncResult<Void>> handler) {
        // 执行客户端
        if (config.getBoolean("startEverything", false)) {
            vertx.eventBus().request(KEY_CLUSTER + EventBusTopicConst.CLI_START_EVERYTHING, null, res -> {
                if (res.failed()) {
                    handler.handle(Future.failedFuture(res.cause()));
                }
            });
        } else if (config.getBoolean("startAllAPP", false)) {
            vertx.eventBus().request(KEY_CLUSTER + EventBusTopicConst.CLI_START_ALL_APP, null, res -> {
                if (res.failed()) {
                    handler.handle(Future.failedFuture(res.cause()));
                }
            });
        } else if (config.getJsonArray("startAPPEverything") != null) {
            vertx.eventBus().request(KEY_CLUSTER + EventBusTopicConst.CLI_START_APP_EVERYTHING, config.getJsonArray("startAPPEverything"), res -> {
                if (res.failed()) {
                    handler.handle(Future.failedFuture(res.cause()));
                }
            });
        } else if (config.getJsonArray("startAPP") != null) {
            vertx.eventBus().request(KEY_CLUSTER + EventBusTopicConst.CLI_START_APP, config.getJsonArray("startAPP"), res -> {
                if (res.failed()) {
                    handler.handle(Future.failedFuture(res.cause()));
                }
            });
        }
        handler.handle(Future.succeededFuture());
    }

    /**
     * 获得配置文件
     */
    public void getConfig(Handler<AsyncResult<JsonObject>> handler) {
        JsonObject contextCfg = config();
        if (Objects.nonNull(contextCfg) && !contextCfg.isEmpty()) {
            handler.handle(Future.<JsonObject>succeededFuture(contextCfg));
            return;
        }
        // 获得系统配置文件
        vertx.fileSystem().readFile(PathUtil.getPathString("conf.json"), res -> {
            if (res.failed()) {
                LOG.error("获取配置文件 失败:", res.cause());
                handler.handle(Future.<JsonObject>failedFuture(res.cause()));
                return;
            }
            try {
                JsonObject config = res.result().toJsonObject();
                if (LOG.isDebugEnabled()) {
                    LOG.debug("执行加载基本配置文件 结果:" + config);
                }
                // 集群配置文件
                JsonObject clusterCfg = config.getJsonObject("cluster", new JsonObject().put("clusterType", CLUSTER_TYPE));
                String clusterType = clusterCfg.getString("clusterType");
                boolean confFromCluster = clusterCfg.getBoolean("getVxApiConfFromCluster", false);
                // 从集群环境中获取应用配置文件
                if (!CLUSTER_TYPE.equals(clusterType) && confFromCluster) {
                    loadClusterConfig(handler, clusterType, clusterCfg);
                } else {
                    // 从配置中获取应用配置文件
                    loadJsonFileConfig(handler, config);
                }
            } catch (Exception e) {
                LOG.error("获取配置文件 失败:", e);
                handler.handle(Future.<JsonObject>failedFuture(e));
            }

        });

    }

    /**
     * 获得默认的数据配置文件
     *
     * @param handler 配置文件加载完成后的回调
     * @param config  配置文件
     */
    private void loadJsonFileConfig(Handler<AsyncResult<JsonObject>> handler, JsonObject config) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("执行从基本配置中加载配置文件...");
        }
        JsonObject nextConf = new JsonObject();
        nextConf.put("verticleConfig", config.getJsonObject("verticleConfig", new JsonObject()));
        nextConf.put("dataConfig", config.getJsonObject("dataConfig", getDefaultDataConfig()));
        nextConf.put("clientConfig", config.getJsonObject("clientConfig", getDefaultClientConfig()));
        nextConf.put("cliConfig", config.getJsonObject("cliConfig", getDefaultClientConfig()));
        if (LOG.isDebugEnabled()) {
            LOG.debug("执行从基本配置中加载配置文件 结果:" + nextConf);
        }
        handler.handle(Future.succeededFuture(nextConf));
    }

    /**
     * 获得默认的数据配置文件
     *
     * @param confHandler 配置文件加载完成后的回调
     * @param clusterType 集群类型
     * @param clusterCfg  集群配置
     * @throws ClassNotFoundException 类不存在
     */
    private void loadClusterConfig(Handler<AsyncResult<JsonObject>> confHandler, String clusterType, JsonObject clusterCfg) throws ClassNotFoundException {
        LOG.debug("执行从集群环境中加载配置文件...");
        // 获取集群环境中的配置文件
        Cluster clusterConfig = ClusterFactory.getClusterConfig(clusterType,
                clusterCfg.getJsonObject("clusterConf", getDefaultClusterConfig()), vertx);
        clusterConfig.getConfig(handler -> {
            if (handler.failed()) {
                LOG.error("执行从集群环境中加载配置文件 失败:" + handler.cause());
                confHandler.handle(Future.failedFuture(handler.cause()));
                return;
            }
            JsonObject result = handler.result();
            if (result != null && !result.isEmpty()) {
                LOG.debug("执行从集群环境中加载配置文件 结果:" + result);
                confHandler.handle(Future.succeededFuture(result));
            }
        });
    }

    /**
     * 获得默认的CLI配置文件
     *
     * @return JsonObject 配置文件
     */
    public JsonObject getDefaultCLIConfig() {
        JsonObject json = new JsonObject();
        json.put("startEverything", false);
        json.put("startAllAPP", false);
        return json;
    }

    /**
     * 获得默认的集群配置文件
     *
     * @return JsonObject 配置文件
     */
    public JsonObject getDefaultClusterConfig() {
        JsonObject json = new JsonObject();
        json.put("zookeeperHosts", "127.0.0.1");
        json.put("sessionTimeout", 20000);
        json.put("connectTimeout", 3000);
        json.put("rootPath", "io.vertx");
        json.put("expApiConfPath", "/io.vertx/exp.api.gateway/conf");
        JsonObject retry = new JsonObject();
        retry.put("initialSleepTime", 100);
        retry.put("intervalTimes", 10000);
        retry.put("maxTimes", 5);
        json.put("retry", retry);
        return json;
    }

    /**
     * 获得默认的数据库配置
     *
     * @return JsonObject 配置文件
     */
    public JsonObject getDefaultDataConfig() {
        return new JsonObject().put("url", "jdbc:sqlite:configDB.db").put("driver_class", "org.sqlite.JDBC");
    }

    /**
     * 获得默认的客户端配置
     *
     * @return JsonObject 配置文件
     */
    public JsonObject getDefaultClientConfig() {
        return new JsonObject().put("clientPort", 5256);
    }

}
